package org.reliablesource.prism.gef.editor.model;

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import org.eclipse.draw2d.Bendpoint;
import org.eclipse.draw2d.Graphics;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.views.properties.ComboBoxPropertyDescriptor;
import org.eclipse.ui.views.properties.IPropertyDescriptor;
import org.eclipse.ui.views.properties.IPropertySource;
import org.eclipse.ui.views.properties.PropertyDescriptor;
import org.reliablesource.prism.core.IPrismElement;
import org.reliablesource.prism.core.internal.ModelElement;

public class Wire extends ModelElement implements IPropertySource {
	
	private static final long serialVersionUID = 2700336516518529686L;

	private static final IPropertyDescriptor[] descriptors = new IPropertyDescriptor[6];
	
	/**
	 * Used for indicating that a PointerPath with dashed line style should be
	 * created.
	 * 
	 * @see org.eclipse.gef.examples.shapes.parts.ShapeEditPart#createEditPolicies()
	 */
	public static final Integer DASHED_CONNECTION = new Integer(
			Graphics.LINE_DASH);

	public static final Integer SOLID_CONNECTION = new Integer(
			Graphics.LINE_SOLID);

	/** Property ID to use when the line style of this connection is modified. */
	public static final String LINESTYLE_PROP = "LineStyle";

	private static final String SOLID_STR = "Solid";

	private static final String DASHED_STR = "Dashed";
	
	private static final String NAME = "Wire.Name";
	
	private static final String BEND_PTS = "Wire.BendPoints";
	private static final String NUM_BEND_PTS = "Wire.nBendPoints";
	private static final String START_LOC = "Wire.StartLocation";
	private static final String END_LOC = "Wire.EndLocation";

	/** True, if the connection is attached to its endpoints. */
	private boolean isConnected;

	/** Line drawing style for this connection. */
	private int lineStyle = Graphics.LINE_SOLID;

	/** PointerPath's source endpoint. */
	private PointerLocation source;

	/** PointerPath's target endpoint. */
	private PointerLocation target;
	
	class PropertySourceLabelProvider implements ILabelProvider{
		StringBuffer buffer = new StringBuffer();
		public Image getImage(Object element) {
			return null;
		}

		public String getText(Object element) {
			buffer.delete(0, buffer.toString().trim().length());
			if (element instanceof PointerLocation) {
				PointerLocation loc = (PointerLocation) element;
				StringBuffer buffer = new StringBuffer();
				buffer.append("(" + loc.getJLocation().x + ","
						+ loc.getJLocation().y + " )");
				return buffer.toString().trim();
			}else if(element instanceof List){
				List bpList = (List)element;
				Object [] list = bpList.toArray();
				for(int i = 0; i < list.length; i++){
					WireBendpoint wpb = (WireBendpoint) list[i];
					buffer.append(wpb.getLocation());
					buffer.append( ",  " );
				}
				return buffer.toString().trim();
			}else if(element instanceof WireBendpoint){
				WireBendpoint wpb = (WireBendpoint)element;
				buffer.append(wpb.getLocation());
				return buffer.toString().trim();
			}
			return buffer.toString().trim();
		}

		public void addListener(ILabelProviderListener listener) {
		}

		public void dispose() {
		}

		public boolean isLabelProperty(Object element, String property) {
			return false;
		}

		public void removeListener(ILabelProviderListener listener) {
		}
		
	};

	
	static {
		descriptors[0] = new ComboBoxPropertyDescriptor(LINESTYLE_PROP,
				LINESTYLE_PROP, new String[] { SOLID_STR, DASHED_STR });		
		descriptors[1] = new PropertyDescriptor(NAME, "Name:");
		descriptors[2] = new PropertyDescriptor(NUM_BEND_PTS, "Num. of Bend Points:");
		descriptors[4] = new PropertyDescriptor(START_LOC, "Start Location:");
		descriptors[3] = new PropertyDescriptor(END_LOC, "End Location:");
		descriptors[5] = new PropertyDescriptor(BEND_PTS, "Bend Point List:");

	}

	protected boolean value;

	protected String sourceTerminal, targetTerminal;

	protected List bendpoints = new ArrayList();

	/**
	 * Create a (solid) connection between two distinct shapes.
	 * 
	 * @param source
	 *            a source endpoint for this connection (non null)
	 * @param target
	 *            a target endpoint for this connection (non null)
	 * @throws IllegalArgumentException
	 *             if any of the parameters are null or source == target
	 * @see #setLineStyle(int)
	 */
	public Wire(final PointerLocation source, final PointerLocation target) {
		reconnect(source, target);
		
		final ILabelProvider propLabelProvider = new PropertySourceLabelProvider();
		((PropertyDescriptor)descriptors[3]).setLabelProvider(propLabelProvider);
		((PropertyDescriptor)descriptors[4]).setLabelProvider(propLabelProvider);
		((PropertyDescriptor)descriptors[5]).setLabelProvider(propLabelProvider);
	}

	public void attachSource() {
		if (getSource() == null
				|| getSource().getSourceWireConnections().contains(this))
			return;
		getSource().addConnection(this);
	}

	public void attachTarget() {
		if (getTarget() == null
				|| getTarget().getTargetWireConnections().contains(this))
			return;
		getTarget().addConnection(this);
	}

	public void detachSource() {
		if (getSource() == null)
			return;
		getSource().removeConnection(this);
	}

	public void detachTarget() {
		if (getTarget() == null)
			return;
		getTarget().removeConnection(this);
	}

	public List getBendpoints() {
		return bendpoints;
	}

	public PointerLocation getSource() {
		return source;
	}

	public String getSourceTerminal() {
		return sourceTerminal;
	}

	public PointerLocation getTarget() {
		return target;
	}

	public String getTargetTerminal() {
		return targetTerminal;
	}

	public boolean getValue() {
		return value;
	}

	public void insertBendpoint(final int index, final Bendpoint point) {
		getBendpoints().add(index, point);
		firePropertyChange("bendpoint", null, null);//$NON-NLS-1$
	}

	public void removeBendpoint(final int index) {
		getBendpoints().remove(index);
		firePropertyChange("bendpoint", null, null);//$NON-NLS-1$
	}

	public void setBendpoint(final int index, final Bendpoint point) {
		getBendpoints().set(index, point);
		firePropertyChange("bendpoint", null, null);//$NON-NLS-1$
	}

	public void setBendpoints(final Vector points) {
		bendpoints = points;
		firePropertyChange("bendpoint", null, null);//$NON-NLS-1$
	}

	public void setSource(final PointerLocation e) {
		Object old = source;
		source = e;
		firePropertyChange("source", old, source);//$NON-NLS-1$
	}

	public void setSourceTerminal(final String s) {
		Object old = sourceTerminal;
		sourceTerminal = s;
		firePropertyChange("sourceTerminal", old, sourceTerminal);//$NON-NLS-1$
	}

	public void setTarget(final PointerLocation e) {
		Object old = target;
		target = e;
		firePropertyChange("target", old, source);//$NON-NLS-1$
	}

	public void setTargetTerminal(final String s) {
		Object old = targetTerminal;
		targetTerminal = s;
		firePropertyChange("targetTerminal", old, sourceTerminal);//$NON-NLS-1$
	}

	public void setValue(final boolean value) {
		if (value == this.value)
			return;
		this.value = value;
		// if (target != null)
		// target.update();
		firePropertyChange("value", null, null);//$NON-NLS-1$
	}

	public String toString() {
//		String label = "Wire(" + getSource() + "," + getSourceTerminal() + "----->" + getTarget() + "," + getTargetTerminal() + ")";
		String label = "Wire(" + getSource().toString() + "==" + "===>" + getTarget().toString() + ")";
		return label;
	}

	public void disconnect() {
		if (isConnected) {
			source.removeConnection(this);
			target.removeConnection(this);
			isConnected = false;
		}

	}

	/*
	 * Reconnect this connection. The connection will reconnect with the
	 * PointerLocations it was previously attached to.
	 */
	public void reconnect() {
		if (!isConnected) {
			source.addConnection(this);
			target.addConnection(this);
			isConnected = true;
		}

	}

	public void reconnect(final PointerLocation src, final PointerLocation tgt) {
		disconnect();
		source = src;
		target = tgt;
		reconnect();
	}

	/*
	 * Set the line drawing style of this connection. @param lineStyle one of
	 * following values: Graphics.LINE_DASH or Graphics.LINE_SOLID
	 * 
	 * @see Graphics#LINE_DASH
	 * @see Graphics#LINE_SOLID @throws IllegalArgumentException if lineStyle
	 *      does not have one of the above values
	 */
	public void setLineStyle(final int lineStyle) {
		if (lineStyle != Graphics.LINE_DASH && lineStyle != Graphics.LINE_SOLID) {
			throw new IllegalArgumentException();
		}
		this.lineStyle = lineStyle;
		firePropertyChange(LINESTYLE_PROP, null, new Integer(this.lineStyle));
	}

	/**
	 * Sets the lineStyle based on the String provided by the PropertySheet
	 * 
	 * @see org.eclipse.ui.views.properties.IPropertySource#setPropertyValue(java.lang.Object,
	 *      java.lang.Object)
	 */
	public void setPropertyValue(final Object id, final Object value) {
		if (id.equals(LINESTYLE_PROP))
			setLineStyle(new Integer(1).equals(value) ? Graphics.LINE_DASH
					: Graphics.LINE_SOLID);
		else
			super.setPropertyValue(id, value);
	}

	/**
	 * Returns the line drawing style of this connection.
	 * 
	 * @return an int value (Graphics.LINE_DASH or Graphics.LINE_SOLID)
	 */
	public int getLineStyle() {
		return lineStyle;
	}

	/**
	 * Returns the descriptor for the lineStyle property
	 * 
	 * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyDescriptors()
	 */
	public IPropertyDescriptor[] getPropertyDescriptors() {
		return descriptors;
	}

	/**
	 * Returns the lineStyle as String for the Property Sheet
	 * 
	 * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyValue(java.lang.Object)
	 */
	public Object getPropertyValue(final Object id) {
		if (id.equals(LINESTYLE_PROP)) {
			if (getLineStyle() == Graphics.LINE_DASH)
				// Dashed is the second value in the combo dropdown
				return new Integer(1);
			// Solid is the first value in the combo dropdown
			return new Integer(0);
		}
		else if(NAME.equals(id)){
			return getName();
		}
		else if(NUM_BEND_PTS.equals(id)){
			return new Integer(getBendpoints().size());
		}
		else if(START_LOC.equals(id)){
			return source;
		}
		else if(END_LOC.equals(id)){
			return target;
		}else if(BEND_PTS.equals(id)){
			return getBendpoints();
		}
		return super.getPropertyValue(id);
	}

	public void setName(String name) {
	}

	public String getName() {
		return toString();
	}

	public IPrismElement getParent() {
		return null;
	}

	public Integer getId() {
		return null;
	}

}
